package org.example.service;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.Refresh;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.util.NamedValue;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.example.config.LocalUerContext;
import org.example.constant.EsConstant;
import org.example.entity.EsDocx;
import org.example.exception.BusinessException;
import org.example.util.EsQueryUtil;
import org.example.util.parser.DocParserFac;
import org.example.util.parser.ParserInter;
import org.example.vo.DocxVo;
import org.example.vo.FileQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DocService {

    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private FileService fileService;
    private final String[] fields = new String[]{"title", "fileName", "fileType", "uploadTime", "fileSize", "userId"};
    private final String[] fieldsWithContent = new String[]{"title", "fileName", "fileType", "uploadTime", "fileSize", "userId", "content"};

    public String insertIntoEs(MultipartFile file) {
        if (StringUtils.isBlank(file.getOriginalFilename())) {
            throw new BusinessException("上传的文件名不能为空");
        }
        EsDocx esDocx = new EsDocx();
        try {
            ParserInter parser = new DocParserFac(file.getOriginalFilename()).getParser();
            DocxVo parse = parser.parse(file);
            String path = fileService.storeFile(file);
            esDocx.setId(UUID.randomUUID().toString());
            esDocx.setContent(parse.getContent());
            esDocx.setTitle(parse.getTitle());
            esDocx.setFileName(file.getOriginalFilename());
            esDocx.setFileSize(file.getSize());
            esDocx.setUploadTime(System.currentTimeMillis());
            esDocx.setUserId(Long.parseLong(LocalUerContext.getUserId()));
            esDocx.setPath(path);
            IndexResponse response = elasticsearchClient.index(i -> i
                    .index(EsConstant.DOC_INDEX)
                    .id(esDocx.getId())
                    .document(esDocx)
                    .refresh(Refresh.True)
            );
            log.info("文档插入成功: ID={}, Index={}", response.id(), response.index());
        } catch (IOException e) {
            throw new BusinessException(e.getMessage());
        }
        return esDocx.getId();
    }


    public List<EsDocx> list(FileQuery fileQuery) {
        EsQueryUtil esQueryUtil = new EsQueryUtil(fileQuery);
        Query boolQuery = BoolQuery.of(b -> b
                .must(esQueryUtil.getQueries())
        )._toQuery();


        SearchRequest.Builder builder = new SearchRequest.Builder()
                .index(EsConstant.DOC_INDEX)
                .query(boolQuery)
                .from(0)
                .size(50)
                .source(sc -> sc.filter(f -> f
                        .includes("id", StringUtils.isNotBlank(fileQuery.getKeyword()) ? fieldsWithContent : fields)
                ));
        if (StringUtils.isNotBlank(fileQuery.getKeyword())) {
            builder.highlight(h -> h
                    .fields(NamedValue.of("content", HighlightField.of(f -> f
                                    .preTags("<em>")           // 高亮前缀
                                    .postTags("</em>")         // 高亮后缀
                                    .fragmentSize(50)         // 单个片段最大长度
                                    .numberOfFragments(1)      // 返回最多3个片段
                            ))
                    )
            );
        }
        log.info("{} query condition is", builder);
        try {
            SearchResponse<EsDocx> response = elasticsearchClient.search(builder.build(), EsDocx.class);
            // 转换为 List<EsDocx>
            return response.hits().hits().stream()
                    .map(hit -> {
                        EsDocx doc = hit.source();
                        if (Objects.isNull(doc)) {
                            return null;
                        }
                        Map<String, List<String>> highlight = hit.highlight();
                        if (highlight.containsKey("content")) {
                            doc.setContent(String.join("。。。", highlight.get("content")));
                        }
                        return doc;
                    })
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public Long deleteFileRecord(List<String> ids) {
        DeleteByQueryRequest deleteByQueryRequest = DeleteByQueryRequest.of(f -> f
                .index(EsConstant.DOC_INDEX)
                .query(q ->
                        q.terms(t ->
                                t.field("id")
                                        .terms(v ->
                                                v.value(ids.stream().map(FieldValue::of).toList()))))
                .refresh(true));
        try {
            DeleteByQueryResponse deleteByQueryResponse = elasticsearchClient.deleteByQuery(deleteByQueryRequest);
            return deleteByQueryResponse.deleted();
        } catch (IOException e) {
            throw new BusinessException(e.getMessage());
        }
    }
}
